﻿#region Legal

// Jimmy Zimmerman
// Team Mongoose
//
// END USER LICENSE AGREEMENT
// IMPORTANT - READ THIS CAREFULLY:  This End User License Agreement is a legal agreement
// between you (either an individual, business entity, currently active identity of an
// individual with Multiple Personality Disorder, octopus overlord, or otherworldly entity),
// and Team Mongoose, for the enclosed, components.
//
// By reading this document and/or installing this product, you agree to be bound by the
// terms of this EULA.
//
// Team Mongoose owns all copyright, trade secret, trademark, trade wars,
// patent, portent, and potential rights to these components.  Team Mongoose
// grants you the right to deploy the enclosed components.
//
// If you agree to the terms of this EULA, a license to use these components is granted to you.
//
// If you should happen to benefit from the enclosed components, then you are legally and
// contractually bound to thank us for it. Send your regards to jimmyzimms@hotmail.com.
//
// OTHER RIGHTS AND LIMITATONS
// You may not reverse-engineer, decompile, decode, disassemble, psycho-analyze, or otherwise
// attempt to find hidden meanings between the lines of this EULA; unless, of course, you should
// happen to find some, and they are funny.
//
// You may not translate this EULA or any part of the components into Ancient Sumerian.
//
// THESE COMPONENTS ARE PROVIDED “AS-IS” WITHOUT WARRANTY OF ANY KIND. ANY USE OF THE COMPONENTS
// CONTAINED IS AT YOUR OWN RISK. TEAM MONGOOSE DISCLAIM ALL WARRANTIES, EITHER
// EXPRESS OR IMPLIED, WITH RESPECT TO THE ACCURRACY AND CORRECTNESS OF THE COMPONENTS CONTAINED
// HEREIN. TEAM MONGOOSE DOES NOT WARRANT THAT THE COMPONENTS ARE FLAWLESS.
//
// REDISTRIBUTION AND USE IN SOURCE AND BINARY FORMS, WITH OR WITHOUT MODIFICATION, ARE PERMITTED
// PROVIDED THAT THE FOLLOWING CONDITIONS ARE MET:
// * REDISTRIBUTIONS OF SOURCE CODE MUST RETAIN THE ABOVE COPYRIGHT NOTICE
// * REDISTRIBUTIONS IN BINARY FORM MUST NOTE THE USE OF THE COMPONENT IN DOCUMENTATION AND/OR
//   OTHER MATERIALS PROVIDED WITH THE DISTRIBUTION.
// * NEITHER THE NAME OF TEAM MONGOOSE MAY BE USED TO ENDORES OR PROMOTE PRODUCTS
//   DERIVED FROM THIS SOFTWARE WITHOUT SPECIFIC PRIOR WRITTEN PERMISSION.
//
// IN NO EVENT SHALL TEAM MONGOOSE BE HELD LIABLE FOR INCIDENTAL, SPECIAL, INDIRECT,
// INCONSEQUENTIAL, UNBELIEVABLE, EXAGGERATED, VERBOSE, OR TYPICAL DAMAGES INCURRED WHILE USING
// THE ENCLOSED COMPONENTS.
//
// OUR STUFF ALWAYS WORKS - SOMETIMES.

#endregion

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;

namespace Mongoose.Games.BfgRoster
{
    /// <summary>
    /// Represents a single vessel (analgous to a model) in the BFG game.
    /// </summary>
    public class Vessel : INotifyPropertyChanged
    {
        #region Fields

        private readonly String name;
        private Int32 leaderShip;
        private readonly ShipClass shipClass;
        private Int32 damagePoints;
        private readonly IList<WeaponsBattery> batteries;
        private readonly IList<OrdinanceBay> ordinance;

        #endregion

        #region Constructor

        /// <summary>
        /// Initializes a new instace of the <see cref="Vessel"/> class.
        /// </summary>
        /// <param name="name">The <see cref="Name"/> of the vessel.</param>
        /// <param name="shipClass">The <see cref="Class"/> of the vessel.</param>
        /// <param name="leadership">The <see cref="Leadership"/> value of the vessel.</param>
        public Vessel(String name, ShipClass shipClass, Int32 leadership)
        {
            name = (name ?? String.Empty).Trim();
            if (name.IsNullOrWhiteSpace()) throw new ArgumentNullException("name");
            if (shipClass == null) throw new ArgumentNullException("shipClass");
            if (leadership < 1) throw new ArgumentOutOfRangeException("leadership", String.Format(null, "Leadership must be at least 1. Actual value '{0}'", leadership));

            this.name = name;
            this.shipClass = shipClass;
            this.leaderShip = leadership;
            this.damagePoints = shipClass.DamageCapacity;
            this.batteries = new ObservableCollection<WeaponsBattery>();
            this.ordinance = new ObservableCollection<OrdinanceBay>();
        }

        #endregion

        #region Properties

        /// <summary>
        /// Gets the name of the vessel.
        /// </summary>
        /// <value>The name of the vessel.</value>
        public virtual String Name
        {
            get { return this.name; }
        }

        /// <summary>
        /// Gets or sets the damage points of the current vessel.
        /// </summary>
        /// <remarks>
        /// <para>
        /// A vessel set to 0 damage points is considered destroyed and can no longer act. Any value
        /// being set less than 0 will be coerced to 0 (you cannot be more destroyed than destroyed).
        /// </para>
        /// <para>Setting this value may change the <see cref="IsCrippled"/> and <see cref="Speed"/> values as well.</para>
        /// <para>Changes to this value will raise the <see cref="PropertyChanged"/> event.</para>
        /// </remarks>
        /// <value>The damage points of the current vessel.</value>
        public virtual Int32 DamagePoints
        {
            get { return this.damagePoints; }
            set
            {
                value = value < 0 ? 0 : value;

                if (!this.IsCrippled)
                {
                    this.damagePoints = value;
                    if (this.IsCrippled)
                    {
                        this.OnCrippled();
                    }
                }
                if (this.damagePoints <= 0)
                {
                    this.OnDestroyed();
                }
                else
                {
                    this.OnPropertyChanged("DamagePoints");
                }
            }
        }

        /// <summary>
        /// Gets or sets the leadership value of the current vessel.
        /// </summary>
        /// <remarks>
        /// <para>
        /// Any attempt to set a leadership value less than 1 will be coerced as 1. Despite what
        /// we would like to think, there's no such thing as NO leadership ;) (or at least in BFG
        /// there is not).
        /// </para>
        /// <para>Changes to this value will raise the <see cref="PropertyChanged"/> event.</para>
        /// </remarks>
        /// <value>The leadershup value of the current vessel.</value>
        public virtual Int32 Leadership
        {
            get { return this.leaderShip; }
            set
            {
                value = value < 1 ? 1 : value;

                if (this.leaderShip == value) return;

                this.leaderShip = value;
                this.OnPropertyChanged("Leadership");
            }
        }

        /// <summary>
        /// Gets the maximum capable speed of the vessel adjusted for damaage and other effects.
        /// </summary>
        /// <remarks>Changes to this value will raise the <see cref="PropertyChanged"/> event.</remarks>
        /// <value>The maximum capable speed of the vessel adjusted for damage and other effects.</value>
        public virtual Int32 Speed
        {
            get
            {
                var result = (this.IsCrippled ? this.Class.Speed - 5 : this.Class.Speed);
                if (result < 0) result = 0;

                return result;
            }
        }

        /// <summary>
        /// Indicates if the ship is currently crippled.
        /// </summary>
        /// <remarks>
        /// <para>A crippled ship has it's targeting ability and movement capacity affected.</para>
        /// <para>Changes to this value will raise the <see cref="PropertyChanged"/> event.</para>
        /// </remarks>
        /// <value>True if the vessel is crippled; otherwise false.</value>
        public virtual Boolean IsCrippled
        {
            get { return (this.Class.DamageCapacity / this.DamagePoints) <= 2; }
        }

        /// <summary>
        /// Gets the vessel class settings.
        /// </summary>
        /// <remarks>
        /// In most games of BFG, this value will NOT vary between instances of the same class.
        /// However, particular classes support upgrades or particular values may change based
        /// on a particular scenario being played. Therefore it should NEVER be assumed that the
        /// particular instance of <see cref="ShipClass"/> is unique between all instances of
        /// <see cref="Vessel"/>. Hitherto, it should NOT be assumed that any instance is shared
        /// between vessels as well. Any acquitition of a <see cref="ShipClass"/> instance MUST
        /// always be made from <see cref="Class"/> property traversal.</remarks>
        /// <value>The vessel class settings.</value>
        public virtual ShipClass Class
        {
            get { return this.shipClass; }
        }

        /// <summary>
        /// Gets the <see cref="IList{T}"/> of <see cref="WeaponsBattery"/> installed in the ship.
        /// </summary>
        /// <value>The <see cref="IList{T}"/> of <see cref="WeaponsBattery"/> installed in the ship.</value>
        public virtual IList<WeaponsBattery> Batteries
        {
            get { return this.batteries; }
        }

        /// <summary>
        /// Gets the <see cref="IList{T}"/> of <see cref="OrdinanceBay"/> installed in the ship.
        /// </summary>
        /// <value>The <see cref="IList{T}"/> of <see cref="OrdinanceBay"/> installed in the ship.</value>
        public virtual IList<OrdinanceBay> Ordinance
        {
            get { return this.ordinance; }
        }

        #endregion

        #region Methods

        /// <summary>
        /// Method called when the vessel is finally destroyed.
        /// </summary>
        protected virtual void OnDestroyed()
        {
        }

        #endregion

        #region Events

        public event EventHandler Crippled;

        /// <summary>
        /// Method called when the vessel is crippled.
        /// </summary>
        /// <remarks>This method will change the <see cref="Speed"/> and <see cref="IsCrippled"/> property values.</remarks>
        protected virtual void OnCrippled()
        {
            this.OnPropertyChanged("IsCrippled");
            this.OnPropertyChanged("Speed");
            
            if (this.Crippled == null) return;

            this.Crippled(this, EventArgs.Empty);
        }

        #endregion

        #region INotifyPropertyChanged Members

        /// <summary>
        /// Raised when the value of a property changes.
        /// </summary>
        public event PropertyChangedEventHandler PropertyChanged;

        /// <summary>
        /// Raises the <see cref="PropertyChanged"/> event.
        /// </summary>
        /// <param name="propertyName">The name of the property that changed.</param>
        protected virtual void OnPropertyChanged(String propertyName)
        {
            if (this.PropertyChanged == null) return;

            this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }

        #endregion
    }
}
